from __future__ import annotations
from abc import ABC
from typing import List, override

__all__ = [
    "Menu", "MenuItem", "MenuComponent", "Waitress"
]

class MenuComponent(ABC):
    def add(self, menu_component: MenuComponent):
        raise NotImplementedError()

    def remove(self, menu_component: MenuComponent):
        raise NotImplementedError()

    def get_child(self, index: int) -> MenuComponent:
        raise NotImplementedError()

    @property
    def name(self) -> str:
        raise NotImplementedError

    @property
    def description(self) -> str:
        raise  NotImplementedError()

    @property
    def price(self) -> float:
        raise NotImplementedError()

    @property
    def is_vegetarian(self) -> bool:
        raise NotImplementedError()

    def print(self):
        raise NotImplementedError()


class Menu(MenuComponent):
    def __init__(self, name: str, description: str):
        self._menu_components: List[MenuComponent] = []
        self._name = name
        self._description = description

    @property
    def menu_components(self):
        return self._menu_components

    @override
    def add(self, menu_component: MenuComponent):
        self._menu_components.append(menu_component)

    @override
    def remove(self, menu_component: MenuComponent):
        self._menu_components.remove(menu_component)

    @override
    def get_child(self, index: int) -> MenuComponent:
        return self._menu_components[index]

    @override
    @property
    def name(self):
        return self._name

    @override
    @property
    def description(self) -> str:
        return self._description

    @override
    def print(self):
        print(f"\n{self.name}, {self.description}\n{''.center(20,'-')}")
        i = iter(self._menu_components)
        while 1:
            try:
                menu_component = next(i)
                menu_component.print()
            except StopIteration:
                break


class MenuItem(MenuComponent):
    def __init__(self, name: str, description: str, vegetarian: bool, price: float):
        self._name = name
        self._description = description
        self._vegetarian = vegetarian
        self._price = price

    @override
    @property
    def name(self):
        return self._name

    @override
    @property
    def description(self):
        return self._description

    @override
    @property
    def price(self):
        return self._price

    @override
    @property
    def is_vegetarian(self):
        return self._vegetarian

    def print(self):
        print(f" {self.name}{'(v)' if self.is_vegetarian else ''}, {self.price}")
        print(f"    -- {self.description}")


class Waitress:
    def __init__(self, all_menus: MenuComponent):
        self._all_menus = all_menus

    def print_menu(self):
        self._all_menus.print()


if __name__ == '__main__':
    pancake_house_menu = Menu("PANCAKE HOUSE MENU", "Breakfast")
    diner_menu = Menu("DINER MENU", "Lunch")
    cafe_menu = Menu("CAFE MENU", "Dinner")
    dessert_menu = Menu("DESSERT MENU", "Dessert of course!")
    coffee_menu = Menu("COFFEE MENU", "Stuff to go with your afternoon coffee")

    all_menus = Menu("ALL MENUS", "All menus combined")

    all_menus.add(pancake_house_menu)
    all_menus.add(diner_menu)
    all_menus.add(cafe_menu)

    pancake_house_menu.add(MenuItem(
        "K&B's Pancake Breakfast",
        "Pancakes with scrambled eggs and toast",
        True,
        2.99
    ))
    pancake_house_menu.add(MenuItem(
        "Regular Pancake Breakfast",
        "Pancakes with fried eggs, sausage",
        False,
        2.99
    ))
    pancake_house_menu.add(MenuItem(
        "Blueberry Pancakes",
        "Pancakes made with fresh blueberries, and blueberry syrup",
        True,
        3.49
    ))
    pancake_house_menu.add(MenuItem(
        "Waffles",
        "Waffles with your choice of blueberries or strawberries",
        True,
        3.59
    ))
    diner_menu.add(MenuItem(
        "Vegetarian BLT",
        "(Fakin') Bacon with lettuce & tomato on whole wheat",
        True,
        2.99
    ))
    diner_menu.add(MenuItem(
        "BLT",
        "Bacon with lettuce & tomato on whole wheat",
        False,
        2.99
    ))
    diner_menu.add(MenuItem(
        "Soup of the day",
        "A bowl of the soup of the day, with a side of potato salad",
        False,
        3.29
    ))
    diner_menu.add(MenuItem(
        "Hot Dog",
        "A hot dog, with saurkraut, relish, onions, topped with cheese",
        False,
        3.05
    ))
    diner_menu.add(MenuItem(
        "Steamed Veggies and Brown Rice",
        "Steamed vegetables over brown rice",
        True,
        3.99
    ))
    diner_menu.add(MenuItem(
        "Pasta",
        "Spaghetti with marinara sauce, and a slice of sourdough bread",
        True,
        3.89
    ))

    diner_menu.add(dessert_menu)

    dessert_menu.add(MenuItem(
        "Apple Pie",
        "Apple pie with a flakey crust, topped with vanilla icecream",
        True,
        1.59
    ))
    dessert_menu.add(MenuItem(
        "Cheesecake",
        "Creamy New York cheesecake, with a chocolate graham crust",
        True,
        1.99
    ))
    dessert_menu.add(MenuItem(
        "Sorbet",
        "A scoop of raspberry and a scoop of lime",
        True,
        1.89
    ))
    dessert_menu.add(MenuItem(
        "Veggie Burger and Air Fries",
        "Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
        True,
        3.99
    ))
    dessert_menu.add(MenuItem(
        "Soup of the day",
        "A cup of the soup of the day, with a side salad",
        False,
        3.69
    ))
    dessert_menu.add(MenuItem(
        "Burrito",
        "A large burrito, with whole pinto beans, salsa, guacamole",
        True,
        4.29
    ))

    cafe_menu.add(coffee_menu)

    coffee_menu.add(MenuItem(
        "Coffee Cake",
        "Crumbly cake topped with cinnamon and walnuts",
        True,
        1.59
    ))
    coffee_menu.add(MenuItem(
        "Bagel",
        "Flavors include sesame, poppyseed, cinnamon raisin, pumpkin",
        False,
        0.69
    ))
    coffee_menu.add(MenuItem(
        "Biscotti",
        "Three almond or hazelnut biscotti cookies",
        True,
        0.89
    ))

    waitress = Waitress(all_menus)

    waitress.print_menu()